package com.batsoft.trade.core;

import com.alibaba.fastjson.JSON;
import com.batsoft.trade.disruptor.opp.OppData;
import com.batsoft.trade.disruptor.opp.OppDataEventQueueHelper;
import com.batsoft.trade.entity.OrderData;
import com.batsoft.trade.model.RedisKey;
import com.batsoft.trade.model.TradeConfig;
import com.batsoft.trade.model.transaction.ContractTrade;
import com.batsoft.trade.model.transaction.Cycle;
import com.gomyb.redis.RedisService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Set;

/**
 * @author <a href="mailto:hellohesir@gmail.com">Mr_He</a>
 * 2019/5/14 00:45
 * 订单撮合
 */
@Service
@Log4j2
public class MatchOrder {

    @Autowired
    private RedisService redisService;

    @Autowired
    private OppDataEventQueueHelper oppDataEventQueueHelper;


    public void match(ContractTrade trade) {
        HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("123", "213");
        redisService.setObj("testobj", objectObjectHashMap);
        long begin = System.currentTimeMillis();
        redisService.execute(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                try {
                    switch (trade.getHandleType()) {
                        case BUY_OPEN_MORE:
                            // 持仓增加->买入
                        case SELL_FLAT_MORE:
                            // 持仓减少->买入
                            matchTransactionBuy(trade, operations);
                            break;
                        case BUY_FLAT_EMPTY:
                            // 持仓减少->卖出
                        case SELL_OPEN_EMPTY:
                            // 持仓增加->卖出
                            matchTransactionSell(trade, operations);
                            break;
                        default:
                    }
                } catch (Exception e) {
                    log.error("trade error ...", e);
                }
                return operations.exec();
            }


        });
        log.info("耗时：{}", System.currentTimeMillis() - begin);

    }

    private void matchTransactionSell(ContractTrade trade, RedisOperations operations) {

    }

    private void matchTransactionBuy(ContractTrade trade, RedisOperations operations) {
        //剩余
        BigDecimal surplus = trade.getCount();
        //已撮合总值
        BigDecimal totalMoney = BigDecimal.ZERO;

        int startIdx = 0;
        boolean isBreak = false;
        while (true) {
            Set<String> orders = rangeOrders(trade, startIdx, trade.getCycle(), trade.getCoinCode());
            if (null == orders || orders.size() == 0) {
                break;
            }
            for (String oNum : orders) {
                // 卖方订单
                OrderData odata = JSON.parseObject(redisService.hGet(RedisKey.getOrderWaitOrder(trade.getCycle(), trade.getCoinCode()), oNum), OrderData.class);
                if (trade.isMarket()) {
                    trade.setPrice(odata.getPrice());
                }
                //卖方委托价
                BigDecimal sPrice = odata.getPrice();
                if (trade.getPrice().compareTo(sPrice) >= 0) {
                    BigDecimal sCount = odata.getCount();
                    if (sCount.compareTo(surplus) > 0) {
                        //卖方部分成交
                        sCount = sCount.subtract(surplus);
                        odata.setCount(sCount);

                        BigDecimal tradeMoney = surplus.multiply(trade.getPrice());
                        odata.setTotalMoney(odata.getTotalMoney().add(tradeMoney));

                        totalMoney = totalMoney.add(tradeMoney);
                        OppData oppData = new OppData();
                        oppData.setCount(surplus);
                        oppData.setPrice(trade.getPrice());
                        oppData.setOData(odata);
                        oppData.setCycle(trade.getCycle());
                        oppDataEventQueueHelper.publishEvent(oppData);

                        surplus = BigDecimal.ZERO;
                        isBreak = true;
                        break;
                    } else {
                        //卖方全部成交
                        surplus = surplus.subtract(odata.getCount());
                        totalMoney = totalMoney.add(sCount.multiply(trade.getPrice()));
                        operations.opsForZSet().remove(RedisKey.getMatchWaitSellOrder(trade.getCycle(), trade.getCoinCode()), oNum);
                        operations.opsForHash().delete(RedisKey.getOrderWaitOrder(trade.getCycle(), trade.getCoinCode()), oNum);
                    }
                } else {
                    isBreak = true;
                    break;
                }
            }
            if (isBreak) {
                break;
            } else {
                startIdx += (TradeConfig.RANGE_COUNT + 1);
            }
        }
        if (surplus.compareTo(trade.getCount()) < 0) {
            if (surplus.compareTo(BigDecimal.ZERO) == 0) {
                // 全部完成 资金操作

            } else {
                addQueue(trade, surplus, totalMoney, operations);
            }
            // 买方持仓统计


        } else {
            addQueue(trade, surplus, totalMoney, operations);
        }
    }

    private void addQueue(ContractTrade trade, BigDecimal surplus, BigDecimal totalMoney, RedisOperations operations) {
        OrderData oData = new OrderData();
        oData.setOrderNum(trade.getOrderNum());
        oData.setUserId(trade.getUid());
        oData.setPrice(trade.getPrice());
        oData.setCount(surplus);
        oData.setCountAll(trade.getCount());
        oData.setHandleType(trade.getHandleType().getValue());
        oData.setFaceValue(trade.getFaceValue());
        oData.setFeeRatio(trade.getFeeRatio());
        oData.setTotalMoney(totalMoney);

        BigDecimal zPrice = trade.getPrice().multiply(TradeConfig.TEN_POW_NUMBER);
        switch (trade.getHandleType()) {
            case BUY_OPEN_MORE:
            case SELL_FLAT_MORE:
                operations.opsForZSet().add(RedisKey.getMatchWaitBuyOrder(trade.getCycle(), trade.getCoinCode()), trade.getOrderNum(), zPrice.doubleValue());
                operations.opsForHash().put(RedisKey.getOrderWaitOrder(trade.getCycle(), trade.getCoinCode()), trade.getOrderNum(), oData);
                break;
            case BUY_FLAT_EMPTY:
            case SELL_OPEN_EMPTY:
                operations.opsForZSet().add(RedisKey.getMatchWaitSellOrder(trade.getCycle(), trade.getCoinCode()), trade.getOrderNum(), zPrice.doubleValue());
                operations.opsForHash().put(RedisKey.getOrderWaitOrder(trade.getCycle(), trade.getCoinCode()), trade.getOrderNum(), oData);
                break;
            default:
        }
    }

    private Set<String> rangeOrders(ContractTrade trade, int startIdx, Cycle cycle, String coinCode) {
        int endIdx = startIdx + TradeConfig.RANGE_COUNT;
        switch (trade.getHandleType()) {
            case BUY_OPEN_MORE:
            case SELL_FLAT_MORE:
                // buy
                return redisService.zRevRange(RedisKey.getMatchWaitBuyOrder(cycle, coinCode), startIdx, endIdx);
            case BUY_FLAT_EMPTY:
            case SELL_OPEN_EMPTY:
                // sell
                return redisService.zRange(RedisKey.getMatchWaitSellOrder(cycle, coinCode), startIdx, endIdx);
            default:
        }
        throw new RuntimeException();
    }

}
